<!doctype html>
<!--
  Minimal Mistakes Jekyll Theme 4.19.3 by Michael Rose
  Copyright 2013-2019 Michael Rose - mademistakes.com | @mmistakes
  Free for personal and commercial use under the MIT license
  https://github.com/mmistakes/minimal-mistakes/blob/master/LICENSE
-->
<html lang="zh" class="no-js">
  <head>
    <meta charset="utf-8">

<!-- begin _includes/seo.html --><title>RISC-V from Scratch 3 - 峰子的乐园</title>
<meta name="description" content="RISC-V from Scratch 3：写 UART 驱动（1 / 3）">


  <meta name="author" content="丁峰 Feng Ding">


<meta property="og:type" content="article">
<meta property="og:locale" content="zh_CN">
<meta property="og:site_name" content="峰子的乐园">
<meta property="og:title" content="RISC-V from Scratch 3">
<meta property="og:url" content="https://dingfen.github.io/risc-v/2020/07/27/riscv-from-scratch-3.html">


  <meta property="og:description" content="RISC-V from Scratch 3：写 UART 驱动（1 / 3）">



  <meta property="og:image" content="https://dingfen.github.io/assets/img/teaser.jpg">





  <meta property="article:published_time" content="2020-07-27T00:00:00+00:00">





  

  


<link rel="canonical" href="https://dingfen.github.io/risc-v/2020/07/27/riscv-from-scratch-3.html">




<script type="application/ld+json">
  {
    "@context": "https://schema.org",
    
      "@type": "Person",
      "name": "丁峰 (Feng Ding)",
      "url": "https://dingfen.github.io/"
    
  }
</script>






<!-- end _includes/seo.html -->


<link href="/feed.xml" type="application/atom+xml" rel="alternate" title="峰子的乐园 Feed">

<!-- https://t.co/dKP3o1e -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<script>
  document.documentElement.className = document.documentElement.className.replace(/\bno-js\b/g, '') + ' js ';
</script>

<!-- For all browsers -->
<link rel="stylesheet" href="/assets/css/main.css">

<!--[if IE]>
  <style>
    /* old IE unsupported flexbox fixes */
    .greedy-nav .site-title {
      padding-right: 3em;
    }
    .greedy-nav button {
      position: absolute;
      top: 0;
      right: 0;
      height: 100%;
    }
  </style>
<![endif]-->



    <!-- start custom head snippets -->

<!-- insert favicons. use https://realfavicongenerator.net/ -->

<!-- end custom head snippets -->

  </head>

  <body class="layout--single categories">
    <nav class="skip-links">
  <h2 class="screen-reader-text">Skip links</h2>
  <ul>
    <li><a href="#site-nav" class="screen-reader-shortcut">Skip to primary navigation</a></li>
    <li><a href="#main" class="screen-reader-shortcut">Skip to content</a></li>
    <li><a href="#footer" class="screen-reader-shortcut">Skip to footer</a></li>
  </ul>
</nav>

    <!--[if lt IE 9]>
<div class="notice--danger align-center" style="margin: 0;">You are using an <strong>outdated</strong> browser. Please <a href="https://browsehappy.com/">upgrade your browser</a> to improve your experience.</div>
<![endif]-->

    

<div class="masthead">
  <div class="masthead__inner-wrap">
    <div class="masthead__menu">
      <nav id="site-nav" class="greedy-nav">
        
        <a class="site-title" href="/">
          峰子的乐园
          <span class="site-subtitle">ideas and techs worth learning and spreading</span>
        </a>
        <ul class="visible-links"><li class="masthead__menu-item">
              <a href="/">Home</a>
            </li><li class="masthead__menu-item">
              <a href="/home/about">About</a>
            </li><li class="masthead__menu-item">
              <a href="/home/blog">Blogs</a>
            </li><li class="masthead__menu-item">
              <a href="/categories">Categories</a>
            </li><li class="masthead__menu-item">
              <a href="https://google.com">External Link</a>
            </li></ul>
        
        <button class="search__toggle" type="button">
          <span class="visually-hidden">Toggle search</span>
          <svg class="icon" width="16" height="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15.99 16">
            <path d="M15.5,13.12L13.19,10.8a1.69,1.69,0,0,0-1.28-.55l-0.06-.06A6.5,6.5,0,0,0,5.77,0,6.5,6.5,0,0,0,2.46,11.59a6.47,6.47,0,0,0,7.74.26l0.05,0.05a1.65,1.65,0,0,0,.5,1.24l2.38,2.38A1.68,1.68,0,0,0,15.5,13.12ZM6.4,2A4.41,4.41,0,1,1,2,6.4,4.43,4.43,0,0,1,6.4,2Z" transform="translate(-.01)"></path>
          </svg>
        </button>
        
        <button class="greedy-nav__toggle hidden" type="button">
          <span class="visually-hidden">切换菜单</span>
          <div class="navicon"></div>
        </button>
        <ul class="hidden-links hidden"></ul>
      </nav>
    </div>
  </div>
</div>


    <div class="initial-content">
      
  







<div class="page__hero--overlay"
  style=" background-image: url('/assets/img/teaser.jpg');"
>
  
    <div class="wrapper">
      <h1 id="page-title" class="page__title" itemprop="headline">
        
          RISC-V from Scratch 3

        
      </h1>
      
        <p class="page__lead">RISC-V from Scratch 3：写 UART 驱动（1 / 3）
</p>
      
      
        <p class="page__meta"><i class="far fa-clock" aria-hidden="true"></i> 




  4 minutes read

</p>
      
      
      
    </div>
  
  
</div>





<div id="main" role="main">
  
  <div class="sidebar sticky">
  


<div itemscope itemtype="https://schema.org/Person">

  
    <div class="author__avatar">
      
        <img src="/assets/img/avatar.jpg" alt="丁峰 Feng Ding" itemprop="image">
      
    </div>
  

  <div class="author__content">
    
      <h3 class="author__name" itemprop="name">丁峰 Feng Ding</h3>
    
    
      <div class="author__bio" itemprop="description">
        <p>Programmer, Graduate majored in CS</p>

      </div>
    
  </div>

  <div class="author__urls-wrapper">
    <button class="btn btn--inverse">follow</button>
    <ul class="author__urls social-icons">
      
        <li itemprop="homeLocation" itemscope itemtype="https://schema.org/Place">
          <i class="fas fa-fw fa-map-marker-alt" aria-hidden="true"></i> <span itemprop="name">Hefei, Anhui, China</span>
        </li>
      

      
        
          
            <li><a href="df12138@mail.ustc.edu.cn" rel="nofollow noopener noreferrer"><i class="fas fa-fw fa-envelope-square" aria-hidden="true"></i><span class="label">Email</span></a></li>
          
        
          
            <li><a href="https://github.com/dingfen" rel="nofollow noopener noreferrer"><i class="fab fa-fw fa-github" aria-hidden="true"></i><span class="label">GitHub</span></a></li>
          
        
      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      

      <!--
  <li>
    <a href="http://link-to-whatever-social-network.com/user/" itemprop="sameAs" rel="nofollow noopener noreferrer">
      <i class="fas fa-fw" aria-hidden="true"></i> Custom Social Profile Link
    </a>
  </li>
-->
    </ul>
  </div>
</div>

  
  </div>



  <article class="page" itemscope itemtype="https://schema.org/CreativeWork">
    <meta itemprop="headline" content="RISC-V from Scratch 3">
    <meta itemprop="description" content="RISC-V from Scratch 3：写 UART 驱动（1 / 3）">
    <meta itemprop="datePublished" content="2020-07-27T00:00:00+00:00">
    

    <div class="page__inner-wrap">
      

      <section class="page__content" itemprop="text">
        
          <aside class="sidebar__right sticky">
            <nav class="toc">
              <header><h4 class="nav__title"><i class="fas fa-file-alt"></i> 目录</h4></header>
              <ul class="toc__menu">
  <li><a href="#risc-v-from-scratch-3-写-uart-驱动">RISC-V from scratch 3: 写 UART 驱动</a>
    <ul>
      <li><a href="#简介">简介</a></li>
      <li><a href="#搭建环境">搭建环境</a></li>
      <li><a href="#什么是-uart">什么是 UART</a></li>
      <li><a href="#硬件布局回顾">硬件布局回顾</a></li>
      <li><a href="#驱动程序的基本框架">驱动程序的基本框架</a></li>
      <li><a href="#设置基础地址">设置基础地址</a></li>
      <li><a href="#接下来">接下来</a></li>
      <li><a href="#我的尝试">我的尝试</a></li>
    </ul>
  </li>
</ul>

            </nav>
          </aside>
        
        <h1 id="risc-v-from-scratch-3-写-uart-驱动">RISC-V from scratch 3: 写 UART 驱动</h1>

<p>今天为大家继续翻译 <em>RISC-V from scratch</em> 系列博客，接着上一部分内容，我们本此的目标是实现 UART 协议的驱动程序，继续完善 RISC-V 的内核。本文译自 <a href="https://twilco.github.io/riscv-from-scratch/2019/07/08/riscv-from-scratch-3.html">RISC-V from scratch 3: Writing a UART driver in nasm (1 / 3)</a> 。</p>

<p>由于我发现该系列的原作者貌似没有把这一系列完成就咕咕了，<strong>因此从本文开始，我将加上一些自己实践的内容，以及一些自己的想法，同大家探讨，算是狗尾续貂，弥补遗憾</strong>。</p>

<h2 id="简介">简介</h2>

<p>欢迎再次来到 <em>RISC-V from scratch</em> ，先快速回顾一下我们之前做过的内容，我们之前已经探索了很多与 RISC-V 及其生态相关的底层概念（例如编译、链接、原语运行时、汇编等）。具体来说，在上一篇文章中，我们使用 <code class="language-plaintext highlighter-rouge">dtc</code> 工具检查了 <code class="language-plaintext highlighter-rouge">virt</code>  <code class="language-plaintext highlighter-rouge">QEMU</code> 虚拟机中的硬件布局，确定了 <code class="language-plaintext highlighter-rouge">RAM</code> 在该计算机中的存放地址，如果你观察仔细的话，会发现 <code class="language-plaintext highlighter-rouge">virt</code> 还有很多有趣的地方，其中一个是 <code class="language-plaintext highlighter-rouge">UART</code>。</p>

<p>为了进一步学习 RISC-V 汇编的知识，我们将在接下来的三篇文章中为该 UART 编写驱动程序，深入探索 ABI，函数以及其中的底层堆栈操作等重要概念。</p>

<p>译注：由于原作者说的三篇文章中的最后一篇还未完成，而译者认为使用 RISC-V 汇编写 UART 驱动程序是吃力不讨好的行为，因此，译者使用 C 语言完成了驱动的编写，以后的内容也会介绍。</p>

<h2 id="搭建环境"><a href="https://dingfen.github.io/2020/07/24/riscv-from-scratch-1.html#qemu-and-risc-v-toolchain-setup">搭建环境</a></h2>

<p>如果你还未看本系列博客的第一部分，没有安装 <code class="language-plaintext highlighter-rouge">riscv-qemu</code> 和 RISC-V 工具链，那么赶紧点击上面标题的链接，跳转到 <a href="https://twilco.github.io/riscv-from-scratch/2019/03/10/riscv-from-scratch-1.html#qemu-and-risc-v-toolchain-setup">“QEMU and RISC-V toolchain setup”</a> 。</p>

<p>之后，再将博主创建的 github 库下载下来，作为我们的工作点。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git@github.com:twilco/riscv-from-scratch.git
<span class="c"># or `git clone https://github.com/twilco/riscv-from-scratch.git` to clone</span>
<span class="c"># via HTTPS rather than SSH</span>
<span class="c"># alternatively, if you are a GitHub user, you can fork this repo.</span>
<span class="c"># https://help.github.com/en/articles/fork-a-repo</span>
<span class="nb">cd </span>riscv-from-scratch/work
</code></pre></div></div>

<p>译注：亲测无需下载 github 库也可实现下面的实验。</p>

<h2 id="什么是-uart">什么是 UART</h2>

<p>UART 是 “<strong>U</strong>niversal <strong>A</strong>synchronous <strong>R</strong>eceiver-<strong>T</strong>ransmitter” 的缩写，它是用于传输、接收系列数据的硬件设备。串行数据传输是逐位顺序发送数据的过程。 相反，并行数据传输是一次发送多个位的过程。 关于串行并行通信，此图很好地说明了差异：</p>

<p><img src="https://twilco.github.io/assets/img/riscv-from-scratch/pt-3/Parallel_and_Serial_Transmission.gif" alt="" class="align-center" /></p>

<p>UART 从不指定数据接收或发送的速率（也称为时钟速率或时钟信号），这是它们异步而不是同步的原因。正因为异步的要求，UART 使用开始和停止位来将数据截断为帧，开始位和停止位会告诉 UART 何时开始和停止读取数据。</p>

<p>你可能听说过 USARTs (<strong>U</strong>niversal <strong>S</strong>ynchronous/<strong>A</strong>synchronous <strong>R</strong>eceiver-<strong>T</strong>ransmitter) ，该设备既可以同步也可以异步工作，当同步工作时，USART 会放弃使用开始位和停止位，而是在单独的线路上发送时钟信号，实现发送与接受的同步。</p>

<p>事实上，UART和USART随处可见。 它们内置于几乎所有现代微控制器（包括我们的虚拟机）中。 这些设备工作在交通信号灯、冰箱以及绕地球轨道运行了多年的卫星上。</p>

<h2 id="硬件布局回顾">硬件布局回顾</h2>

<p>在我们正式开始写驱动前，我们需要一些额外的信息来解决一些问题。我们如何配置虚拟机的 UART ？ 我们可以在哪个内存地址找到接收和发送缓冲区？</p>

<p>接下来，我们使用 <code class="language-plaintext highlighter-rouge">dtc</code> 工具，回顾一下 <code class="language-plaintext highlighter-rouge">uart</code> 的 devicetree 节点的一些信息。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Install 'dtc' if you don't already have it.</span>
<span class="c"># I use 'brew' for MacOS - you may need to do something else.</span>
brew <span class="nb">install </span>dtc
<span class="c"># Use qemu to dump info about the 'virt' machine in dtb (device tree blob) </span>
<span class="c"># format.</span>
<span class="c"># The data in this file represents hardware components of a given </span>
<span class="c"># machine / device / board.</span>
qemu-system-riscv64 <span class="nt">-machine</span> virt <span class="nt">-machine</span> <span class="nv">dumpdtb</span><span class="o">=</span>riscv64-virt.dtb
<span class="c"># Convert our .dtb into a human-readable .dts (device tree source) file.</span>
dtc <span class="nt">-I</span> dtb <span class="nt">-O</span> dts <span class="nt">-o</span> riscv64-virt.dts riscv64-virt.dtb
<span class="c"># Search for 'uart' and display 2 lines before and 6 lines after each match.</span>
<span class="nb">grep </span>uart riscv64-virt.dts <span class="nt">-B</span> 2 <span class="nt">-A</span> 6
        chosen <span class="o">{</span>
                bootargs <span class="o">=</span> <span class="o">[</span>00]<span class="p">;</span>
                stdout-path <span class="o">=</span> <span class="s2">"/uart@10000000"</span><span class="p">;</span>
        <span class="o">}</span><span class="p">;</span>
<span class="nt">--</span>
        <span class="o">}</span><span class="p">;</span>

        uart@10000000 <span class="o">{</span>
                interrupts <span class="o">=</span> &lt;0x0a&gt;<span class="p">;</span>
                interrupt-parent <span class="o">=</span> &lt;0x02&gt;<span class="p">;</span>
                clock-frequency <span class="o">=</span> &lt;0x384000&gt;<span class="p">;</span>
                reg <span class="o">=</span> &lt;0x00 0x10000000 0x00 0x100&gt;<span class="p">;</span>
                compatible <span class="o">=</span> <span class="s2">"ns16550a"</span><span class="p">;</span>
        <span class="o">}</span><span class="p">;</span>
</code></pre></div></div>

<p>在 <code class="language-plaintext highlighter-rouge">grep</code> 输出的最上面，<code class="language-plaintext highlighter-rouge">chosen</code> 节点出现了，该节点内容表明，输出信息会通过 UART 设备打印出来。根据此篇<a href="https://elinux.org/Device_Tree_Usage#chosen_Node">文档</a>，<code class="language-plaintext highlighter-rouge">chosen</code> 节点不代表任何物理硬件设备，通常用于在固件和运行在裸机上的程序（比如操作系统）之间的数据交换，我们接下来的操作不需要用到该节点，不必理会。</p>

<p>接下来才是我们想要的东西—— <code class="language-plaintext highlighter-rouge">uart</code> 节点。根据前面的知识，我们很容易就发现 UART 的内存地址位于 <code class="language-plaintext highlighter-rouge">0x10000000</code> ，还有 <code class="language-plaintext highlighter-rouge">interrupts</code> 和 <code class="language-plaintext highlighter-rouge">interrupt-parent</code> 属性，表示 UART 是会产生中断的。</p>

<p>可能有读者不太熟悉计算机系统，因此我这里简单介绍一下中断 <code class="language-plaintext highlighter-rouge">interrupt</code>，中断是硬件或软件向处理器发出的信号，指示事件需要立即处理执行。例如，在以下情况下，UART 可能会产生中断：</p>

<ul>
  <li>新的数据进入了接收缓存</li>
  <li>数据传送机 (transmitter) 完成了缓存中数据的发送</li>
  <li>UART 遇到了发送错误的情况</li>
</ul>

<p>这些中断行为充当 hook ，程序员可编写代码适当地响应这些事件，不过接下来的内容我们不会用到中断，因此先忽略到这些内容吧。</p>

<p>再来看一下 <code class="language-plaintext highlighter-rouge">clock-frequency = &lt;0x38400&gt;</code>  ，参考 <a href="https://buildmedia.readthedocs.org/media/pdf/devicetree-specification/latest/devicetree-specification.pdf">devicetree specification</a> ，<code class="language-plaintext highlighter-rouge">clock-frequency</code> 代表了时钟的初始频率，其值为十六进制的 <code class="language-plaintext highlighter-rouge">0x38400</code> Hz ，即3.6864 MHz，每秒36.864百万个时钟滴答，这是标准的晶体振荡器频率。</p>

<p>下一个属性就很熟悉了 <code class="language-plaintext highlighter-rouge">reg = &lt;0x00 0x10000000 0x00 0x100&gt;</code> ，决定了 UART 的内存位置，以及它的长度，<a href="https://dingfen.github.io/risc-v/2020/07/26/riscv-from-scratch-2.html">在上一篇文章中</a>，我们知道有两个 32-bit 的值在描述信息。通过给的信息来看，不难得出 UART 的内存位置起始于 <code class="language-plaintext highlighter-rouge">0x00 + 0x10000000 = 0x10000000</code> ，且长度为 <code class="language-plaintext highlighter-rouge">0x00 + 0x100 = 0x100</code> 字节。</p>

<p><code class="language-plaintext highlighter-rouge">uart</code> 节点的最后一个属性，<code class="language-plaintext highlighter-rouge">compatible =“ ns16550a” ;</code>，它告知我们 UART 与哪种编程模型兼容。 操作系统使用此属性来确定其可用于外围设备的设备驱动程序。网上有很多的实现与 NS16550A 兼容的 UART 所需的资料，<a href="https://www.lammertbies.nl/comm/info/serial-uart">这篇</a>是本文所引用的。</p>

<h2 id="驱动程序的基本框架">驱动程序的基本框架</h2>

<p>现在，我们创建新文件，取名 <code class="language-plaintext highlighter-rouge">ns16550a.s</code> ，在这里我们开始构建驱动程序的基本框架，首先，我们仅仅先实现一个读写字符的函数，不管那些复杂的中断。</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.global</span> <span class="nv">uart_put_char</span>
<span class="nf">.global</span> <span class="nv">uart_get_char</span>

<span class="nl">uart_get_char:</span>
    <span class="nf">.cfi_startproc</span>
    <span class="nf">.cfi_endproc</span>

<span class="nl">uart_put_char:</span>
    <span class="nf">.cfi_startproc</span>
    <span class="nf">.cfi_endproc</span>

<span class="nf">.end</span>
</code></pre></div></div>

<p>我们从 <code class="language-plaintext highlighter-rouge">.global</code> 汇编指令开始，将 <code class="language-plaintext highlighter-rouge">uart_put_char</code> 和 <code class="language-plaintext highlighter-rouge">uart_get_char</code> 声明为其他文件可访问的符号。以 <code class="language-plaintext highlighter-rouge">.</code> 开头的指令都是伪指令，它们只向汇编器提供信息，不是可执行代码。所有基本 GNU 汇编器指令的详细说明都可以在<a href="https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html">这里</a>找到。</p>

<p>接下来，将会有每个符号的定义，当前仅包含 <code class="language-plaintext highlighter-rouge">.cfi</code> 汇编程序指令。这些 <code class="language-plaintext highlighter-rouge">.cfi</code> 指令将框架的结构及其展开方法通知工具（例如汇编器或异常展开器）。<code class="language-plaintext highlighter-rouge">.cfi_startproc</code> 和 <code class="language-plaintext highlighter-rouge">.cfi_endproc</code> 分别表示函数的开始和结束。</p>

<p>尽管我们还没有完全开始写驱动（你肯定能察觉到我们只是搭建了个框架），我们先把他编译一下，看看这个框架是否可用。</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>riscv64-unknown-elf-gcc <span class="nt">-g</span> <span class="nt">-ffreestanding</span> <span class="nt">-O0</span> <span class="nt">-Wl</span>,--gc-sections <span class="se">\</span>
    <span class="nt">-nostartfiles</span> <span class="nt">-nostdlib</span> <span class="nt">-nodefaultlibs</span> <span class="nt">-Wl</span>,-T,riscv64-virt.ld <span class="se">\</span>
    crt0.s ns16550a.s
</code></pre></div></div>

<p>如果你很想知道这些编译选项是什么意思，建议参考<a href="https://twilco.github.io/riscv-from-scratch/2019/04/27/riscv-from-scratch-2.html#debugging-but-for-real-this-time">这里</a>。</p>

<p>然后，我们得到了一个错误：</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/Users/twilco/usys/riscv/riscv64-unknown-elf-gcc-8.2.0-2019.02.0-x86_64-apple-darwin/bin/../lib/gcc/riscv64-unknown-elf/8.2.0/../../../../riscv64-unknown-elf/bin/ld: /var/folders/rg/hbr8vy7d13z9k7pdn0l_n9z51y1g13/T//ccjYQiJc.o: <span class="k">in function</span> <span class="sb">`</span>.L0 <span class="s1">':
/Users/twilco/projects/riscv-from-scratch/work/crt0.s:12: undefined reference to `main'</span>
collect2: error: ld returned 1 <span class="nb">exit </span>status
</code></pre></div></div>

<p>不过，放轻松，只是缺少 <code class="language-plaintext highlighter-rouge">main</code> 函数而已。这是因为在 <code class="language-plaintext highlighter-rouge">crt0.s</code> 文件中，我们曾经用到过 <code class="language-plaintext highlighter-rouge">main</code> 函数的地址：</p>

<div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.section</span> <span class="nv">.init</span><span class="p">,</span> <span class="s">"ax"</span>
<span class="nf">.global</span> <span class="nv">_start</span>
<span class="nl">_start:</span>
    <span class="nf">.cfi_startproc</span>
    <span class="nf">.cfi_undefined</span> <span class="nv">ra</span>
    <span class="nf">.option</span> <span class="nv">push</span>
    <span class="nf">.option</span> <span class="nv">norelax</span>
    <span class="nf">la</span> <span class="nv">gp</span><span class="p">,</span> <span class="nv">__global_pointer$</span>
    <span class="nf">.option</span> <span class="nv">pop</span>
    <span class="nf">la</span> <span class="nb">sp</span><span class="p">,</span> <span class="nv">__stack_top</span>
    <span class="nf">add</span> <span class="nv">s0</span><span class="p">,</span> <span class="nb">sp</span><span class="p">,</span> <span class="nv">zero</span>
    <span class="nf">jal</span> <span class="nv">zero</span><span class="p">,</span> <span class="nv">main</span> <span class="err">#</span> <span class="o">&lt;~~~~~~~~~~</span>
    <span class="nf">.cfi_endproc</span>
    <span class="nf">.end</span>
</code></pre></div></div>

<p>那么，为了简单起见，先创建个文件 <code class="language-plaintext highlighter-rouge">main.c</code> ，然后把 <code class="language-plaintext highlighter-rouge">main</code> 函数的定义写出来：</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">uart_put_char</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>最后，将这几个文件一起编译，就不会报错了：</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>riscv64-unknown-elf-gcc <span class="nt">-g</span> <span class="nt">-ffreestanding</span> <span class="nt">-O0</span> <span class="nt">-Wl</span>,--gc-sections <span class="se">\</span>
    <span class="nt">-nostartfiles</span> <span class="nt">-nostdlib</span> <span class="nt">-nodefaultlibs</span> <span class="nt">-Wl</span>,-T,riscv64-virt.ld <span class="se">\</span>
    crt0.s ns16550a.s main.c
</code></pre></div></div>

<p>除此之外，我们可以使用 <code class="language-plaintext highlighter-rouge">nm</code> 工具，查看一下 <code class="language-plaintext highlighter-rouge">a.out</code> 文件里面符号定义的情况：</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>riscv64-unknown-elf-nm a.out

00000000800010a0 R __BSS_END__
000000008000109c R __DATA_BEGIN__
000000008000109c R __SDATA_BEGIN__
000000008000109c R __bss_start
000000008000189c A __global_pointer<span class="err">$</span>
0000000088000000 T __stack_top
000000008000109c R _edata
00000000800010a0 R _end
0000000080000000 T _start
0000000080000018 T main
0000000080000018 T uart_get_char
0000000080000018 T uart_put_char
</code></pre></div></div>

<h2 id="设置基础地址">设置基础地址</h2>

<p>从这篇<a href="https://www.lammertbies.nl/comm/info/serial-uart.html">资料</a>得知，NS16550A UART 有十二个寄存器，访问每个寄存器只需要在基址的基础上加上若干字节的偏移量即可。为了能方便地访问这些寄存器，我们首先需要定义一个代表该基址的符号。 正如我们从 <code class="language-plaintext highlighter-rouge">riscv64-virt.dts</code> 中发现的那样，基址位于 <code class="language-plaintext highlighter-rouge">0x00 + 0x10000000 = 0x10000000</code>，这就是 <code class="language-plaintext highlighter-rouge">reg</code> 属性中的内容：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>uart@10000000 {
    interrupts = &lt;0x0a&gt;;
    interrupt-parent = &lt;0x02&gt;;
    clock-frequency = &lt;0x384000&gt;;
    reg = &lt;0x00 0x10000000 0x00 0x100&gt;;
    compatible = "ns16550a";
};
</code></pre></div></div>

<p>在 <code class="language-plaintext highlighter-rouge">riscv64-virt.ld</code> 文件中，加入这个符号：</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...more above...
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x10000));
  . = SEGMENT_START("text-segment", 0x10000) + SIZEOF_HEADERS;
  PROVIDE(__stack_top = ORIGIN(RAM) + LENGTH(RAM));
  /* &gt;&gt;&gt;&gt;&gt;&gt; Our newest addition. &lt;&lt;&lt;&lt;&lt;&lt; */
  PROVIDE(__uart_base_addr = 0x10000000);
  /* &gt;&gt;&gt;&gt;&gt;&gt; End of our addition. &lt;&lt;&lt;&lt;&lt;&lt; */
  .interp         : { *(.interp) }
...more below...
</code></pre></div></div>

<p>当 <code class="language-plaintext highlighter-rouge">__uart_base_addr</code> 定义完成后，我们就可以很轻松地访问 NS16550A 的寄存器了！</p>

<h2 id="接下来">接下来</h2>

<p>今天，我们了解了 UART 和 USART 、NS16550A 规范，中断以及一些其他 devicetree 属性。 我们还为UART 组装驱动程序创建了基础框架，并已将 <code class="language-plaintext highlighter-rouge">__uart_base_addr</code> 编码为链接器文件中的符号，以方便对 UART 寄存器访问。</p>

<p>在下一篇文章中，我们将讨论和实现两个驱动程序函数 <code class="language-plaintext highlighter-rouge">uart_get_char</code> 和 <code class="language-plaintext highlighter-rouge">uart_put_char</code> 。 函数是在汇编世界中使函数调用成为可能的重要部分。 我们将逐步介绍函数的序幕，并提供详细说明堆栈更改和每条指令寄存器的图表。</p>

<hr />

<h2 id="我的尝试">我的尝试</h2>

<p>OK！原博文翻译到此结束！现在介绍一下我的实验方案：</p>

<p>事实上，在跟着写完 <code class="language-plaintext highlighter-rouge">crt0.s</code> 文件，并将他们编译、链接，运行在虚拟机上时，我的思想就与原博主最初的想法不太一样了，原博主只是想要探究一下 RISC-V 的底层技术，但我想要做的却是一个 RISC-V 内核。</p>

<p>原博主的实验步骤中，创建 <code class="language-plaintext highlighter-rouge">crt0.s</code> 以及它的前因后果解释非常详细，让我受益良多。但同时我也马上明白，这些步骤只要再稍加调整，就完全可以当作操作系统的启动工作了！那么接下来，我将会继续我自己的实验，敬请期待。</p>


        
      </section>

      <footer class="page__meta">
        
        
  


  
  
  

  <p class="page__taxonomy">
    <strong><i class="fas fa-fw fa-tags" aria-hidden="true"></i> tag: </strong>
    <span itemprop="keywords">
    
      
      
      <a href="/tags/#risc-v" class="page__taxonomy-item" rel="tag">RISC-V</a>
    
    </span>
  </p>




  


  
  
  

  <p class="page__taxonomy">
    <strong><i class="fas fa-fw fa-folder-open" aria-hidden="true"></i> category: </strong>
    <span itemprop="keywords">
    
      
      
      <a href="/categories/#risc-v" class="page__taxonomy-item" rel="tag">RISC-V</a>
    
    </span>
  </p>


        
  <p class="page__date"><strong><i class="fas fa-fw fa-calendar-alt" aria-hidden="true"></i> update time:</strong> <time datetime="2020-07-27T00:00:00+00:00">July 27, 2020</time></p>


      </footer>

      <section class="page__share">
  
    <h4 class="page__share-title">share</h4>
  

  <a href="https://twitter.com/intent/tweet?text=RISC-V+from+Scratch+3%20https%3A%2F%2Fdingfen.github.io%2Frisc-v%2F2020%2F07%2F27%2Friscv-from-scratch-3.html" class="btn btn--twitter" onclick="window.open(this.href, 'window', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0'); return false;" title="share Twitter"><i class="fab fa-fw fa-twitter" aria-hidden="true"></i><span> Twitter</span></a>

  <a href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fdingfen.github.io%2Frisc-v%2F2020%2F07%2F27%2Friscv-from-scratch-3.html" class="btn btn--facebook" onclick="window.open(this.href, 'window', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0'); return false;" title="share Facebook"><i class="fab fa-fw fa-facebook" aria-hidden="true"></i><span> Facebook</span></a>

  <a href="https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fdingfen.github.io%2Frisc-v%2F2020%2F07%2F27%2Friscv-from-scratch-3.html" class="btn btn--linkedin" onclick="window.open(this.href, 'window', 'left=20,top=20,width=500,height=500,toolbar=1,resizable=0'); return false;" title="share LinkedIn"><i class="fab fa-fw fa-linkedin" aria-hidden="true"></i><span> LinkedIn</span></a>
</section>


      
  <nav class="pagination">
    
      <a href="/2020/07/27/minimal-mistakes.html" class="pagination--pager" title="Minimal Mistakes
">previous</a>
    
    
      <a href="/risc-v/2020/08/01/riscv-from-scratch-4.html" class="pagination--pager" title="RISC-V from Scratch 4
">next</a>
    
  </nav>

    </div>

    
  </article>

  
  
    <div class="page__related">
      <h4 class="page__related-title">related</h4>
      <div class="grid__wrapper">
        
          



<div class="grid__item">
  <article class="archive__item" itemscope itemtype="https://schema.org/CreativeWork">
    
      <div class="archive__item-teaser">
        <img src="/assets/img/teaser.jpg" alt="">
      </div>
    
    <h2 class="archive__item-title" itemprop="headline">
      
        <a href="/risc-v/2020/08/17/riscv-from-scratch-6.html" rel="permalink">RISC-V from Scratch 6
</a>
      
    </h2>
    
      <p class="page__meta"><i class="far fa-clock" aria-hidden="true"></i> 




  5 minutes read

</p>
    
    <p class="archive__item-excerpt" itemprop="description">RISC-V from Scratch 6：时钟中断
</p>
  </article>
</div>

        
          



<div class="grid__item">
  <article class="archive__item" itemscope itemtype="https://schema.org/CreativeWork">
    
      <div class="archive__item-teaser">
        <img src="/assets/img/teaser.jpg" alt="">
      </div>
    
    <h2 class="archive__item-title" itemprop="headline">
      
        <a href="/risc-v/2020/08/06/riscv-from-scratch-5.html" rel="permalink">RISC-V from Scratch 5
</a>
      
    </h2>
    
      <p class="page__meta"><i class="far fa-clock" aria-hidden="true"></i> 




  5 minutes read

</p>
    
    <p class="archive__item-excerpt" itemprop="description">RISC-V from Scratch 5：机器模式
</p>
  </article>
</div>

        
          



<div class="grid__item">
  <article class="archive__item" itemscope itemtype="https://schema.org/CreativeWork">
    
      <div class="archive__item-teaser">
        <img src="/assets/img/teaser.jpg" alt="">
      </div>
    
    <h2 class="archive__item-title" itemprop="headline">
      
        <a href="/risc-v/2020/08/05/riscv-privileged.html" rel="permalink">RISC-V 特权架构
</a>
      
    </h2>
    
      <p class="page__meta"><i class="far fa-clock" aria-hidden="true"></i> 




  8 minutes read

</p>
    
    <p class="archive__item-excerpt" itemprop="description">RISC-V 特权架构
</p>
  </article>
</div>

        
          



<div class="grid__item">
  <article class="archive__item" itemscope itemtype="https://schema.org/CreativeWork">
    
      <div class="archive__item-teaser">
        <img src="/assets/img/teaser.jpg" alt="">
      </div>
    
    <h2 class="archive__item-title" itemprop="headline">
      
        <a href="/risc-v/2020/08/01/riscv-from-scratch-4.html" rel="permalink">RISC-V from Scratch 4
</a>
      
    </h2>
    
      <p class="page__meta"><i class="far fa-clock" aria-hidden="true"></i> 




  7 minutes read

</p>
    
    <p class="archive__item-excerpt" itemprop="description">RISC-V from Scratch 4：写 UART 驱动（2 / 3）
</p>
  </article>
</div>

        
      </div>
    </div>
  
  
</div>

    </div>

    
      <div class="search-content">
        <div class="search-content__inner-wrap"><form class="search-content__form" onkeydown="return event.key != 'Enter';">
    <label class="sr-only" for="search">
      Enter your search term...
    </label>
    <input type="search" id="search" class="search-input" tabindex="-1" placeholder="输入您要搜索的关键词..." />
  </form>
  <div id="results" class="results"></div></div>

      </div>
    

    <div id="footer" class="page__footer">
      <footer>
        <!-- start custom footer snippets -->

<!-- end custom footer snippets -->
        <div class="page__footer-follow">
  <ul class="social-icons">
    
      <li><strong>follow:</strong></li>
    

    
      
        
          <li><a href="https://github.com/dingfen" rel="nofollow noopener noreferrer"><i class="fab fa-fw fa-github" aria-hidden="true"></i> Github</a></li>
        
      
        
          <li><a href="df12138@mail.ustc.edu.cn" rel="nofollow noopener noreferrer"><i class="fas fa-fw fa-envelope-square" aria-hidden="true"></i> Email</a></li>
        
      
    

    <li><a href="/feed.xml"><i class="fas fa-fw fa-rss-square" aria-hidden="true"></i> Feed</a></li>
  </ul>
</div>

<div class="page__footer-copyright">&copy; 2020 丁峰 (Feng Ding). Powered by <a href="https://jekyllrb.com" rel="nofollow">Jekyll</a> &amp; <a href="https://mademistakes.com/work/minimal-mistakes-jekyll-theme/" rel="nofollow">Minimal Mistakes</a>.</div>

      </footer>
    </div>

    
  <script src="/assets/js/main.min.js"></script>
  <script src="https://kit.fontawesome.com/4eee35f757.js"></script>




<script src="/assets/js/lunr/lunr.min.js"></script>
<script src="/assets/js/lunr/lunr-store.js"></script>
<script src="/assets/js/lunr/lunr-en.js"></script>




    
  <script>
    var disqus_config = function () {
      this.page.url = "https://dingfen.github.io/risc-v/2020/07/27/riscv-from-scratch-3.html";  /* Replace PAGE_URL with your page's canonical URL variable */
      this.page.identifier = "/risc-v/2020/07/27/riscv-from-scratch-3"; /* Replace PAGE_IDENTIFIER with your page's unique identifier variable */
    };
    (function() { /* DON'T EDIT BELOW THIS LINE */
      var d = document, s = d.createElement('script');
      s.src = 'https://https://dingfen.github.io/.disqus.com/embed.js';
      s.setAttribute('data-timestamp', +new Date());
      (d.head || d.body).appendChild(s);
    })();
  </script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>


  





  </body>
</html>
